#include "language.h"
#include "syntax_char.h"
#include "parse_info.h"
#include <iostream>

using namespace std;

namespace syntax_analysis
{
	syntax_type syntax_char::get_type()
	{
		return SYNTAX_CHAR;
	}
	
	string syntax_char::to_string()
	{
		string syn;
		switch( m_char )
		{
		case '\n':
			syn += "\\n";
			break;
		case '\t':
			syn += "\\t";
			break;
		case '\r':
			syn += "\\r";
			break;
		case ' ':
			syn += "\\p";
			break;
		case '[':
		case ']':
		case '(':
		case ')':
		case '{':
		case '}':
		case '<':
		case '>':
		case ';':
		case '\'':
		case '\"':
		case ',':
		case '\\':
			char buf[3];
			buf[0] = '\\';
			buf[1] = m_char;
			buf[2] = 0;
			syn += buf;
			break;
		default:
			syn += m_char;
			break;
		}
		if( m_attr != NULL )
			syn += m_attr->to_string();
		return syn;
	}
	
	bool syntax_char::reparse( parse_info *info )
	{
		parse_para *para;
		bool result = false;
		
		inc_called();
		
		para = info->get_parse_para();
		if( para == NULL )
			return false;
		para->push_call( this );
		
		if( can_accept_more( para ) )
			result = do_parse( para );
		
		if( !result )
		{
			if( can_accept_more( para ) )
				para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
//			cout << para->get_last_start_code() << endl << "----------------------" << endl;
			para->pop_call();
		}
		
		para->set_parsed();

		return result;
	}

	bool syntax_char::parse( parse_info *info )
	{
		parse_para *para;
		bool result = false;
		
		inc_called();

		para = info->get_parse_para();
		if( para == NULL )
			return false;
		para->push_call( this );

		if( should_accept_more( para ) )
		{
			do 
			{
				result = do_parse( para );
				
			} while( result && should_accept_more( para ) );
		}
		else if( !para->parsed() )
		{
			result = para->add_acceptted( this, 0 );
		}
		
		if( result && 
			( m_attr == NULL ||
			  (m_attr->get_match_type()&MAX_MATCH) == MAX_MATCH ) )
		{
			while( can_accept_more( para ) && do_parse( para ) )
			{
				if( m_attr != NULL && (m_attr->get_match_type()&DONT_TAKE_STRING) == DONT_TAKE_STRING )
					break;
			}
		}

		if( !result )
		{
			if( can_accept_more( para ) )
				para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
//			cout << para->get_last_start_code() << endl << "----------------------" << endl;
			para->pop_call();
		}

		para->set_parsed();

		return result;
	}

	bool syntax_char::do_parse( parse_para *para )
	{
		int last_accept;
		char *code;
		bool result = false;

		last_accept = para->get_acceptted( para->get_acceptted_count() - 1 );
		code = para->get_code();

		if( code[last_accept] == m_char )
		{
			result = true;
			++last_accept;
			if( m_attr == NULL || (m_attr->get_match_type()&DONT_TAKE_STRING) == 0 )
				result = para->add_acceptted( this, last_accept );
			else
				para->add_acceptted( this, last_accept-1, false );
		}
		else
		{
			result = false;
		}
		return result;
	}

	bool syntax_char::can_accept_more( parse_para *para )
	{
		int nmax;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmax = m_attr->get_max();
		if( nmax >= 0 )
			return real < nmax;
		return true;
	}
	
	bool syntax_char::should_accept_more( parse_para *para )
	{
		int nmin;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmin = m_attr->get_min();
		if( nmin >= 0 )
			return real < nmin;
		else if( nmin == EXP_ONE_MORE )
			return real < 1;
		return false;
	}

}